/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { SceneConfig } from '../ArkAnalyzer/src/Config';
import { Scene } from '../ArkAnalyzer/src/Scene';
import { ArkMethodDefStmt, Node, ArkFieldDefStmt } from './Nodes';
import { CfgEdge, DfgEdge, Edge, CgEdge } from './Edge';
import { driver } from 'gremlin';
import { CallGraph } from '../ArkAnalyzer/src/callgraph/model/CallGraph';
import { CallGraphBuilder } from '../ArkAnalyzer/src/callgraph/model/builder/CallGraphBuilder';
import { ArkMethod } from '../ArkAnalyzer/src/core/model/ArkMethod';
import { AbstractInvokeExpr } from '../ArkAnalyzer/src/core/base/Expr';
import { ArkAssignStmt, ArkReturnStmt, ArkReturnVoidStmt, Stmt } from '../ArkAnalyzer/src/core/base/Stmt';
import { Local } from '../ArkAnalyzer/src/core/base/Local';
import { FunctionType } from '../ArkAnalyzer/src/core/base/Type';
import * as fs from 'fs';
import * as path from 'path';
import { AbstractFieldRef, ArkBody, ArkClass, ArkField, ArkFile, ArkInstanceFieldRef, BasicBlock, Decorator, FieldSignature, MethodSignature, MethodSubSignature } from '../ArkAnalyzer/src';
import { DfgBuilder } from './DfgBuilder';
import { logger } from './Logger';


export const DEFAULT_METHOD = "[static]_DEFAULT_ARK_METHOD()";
let MAX_BATCH_SIZE = 100;


export class GraphBuilder {
    private scene: Scene;
    private configPath: string;
    private client: driver.Client;

    private method2MethodStmt: Map<MethodSignature, ArkMethodDefStmt> = new Map<MethodSignature, ArkMethodDefStmt>()
    private invokeStmt2Methods: Map<Stmt, MethodSignature>[] = [];
    private stmt2GremlinId: Map<Stmt, any> = new Map();
    private field2FieldStmt: Map<FieldSignature, ArkFieldDefStmt> = new Map<FieldSignature, ArkFieldDefStmt>();
    private fieldAccessStmtss: FieldAccessStmt[][] = [];

    public getStmt2GremlinId(): Map<Stmt, any> {
        return this.stmt2GremlinId;
    }

    public setScene(scene: Scene) {
        this.scene = scene;
    }

    public getScene() {
        return this.scene;
    }

    constructor(client: driver.Client, configPath?: string) {
        this.client = client;
        if (configPath === undefined) {
            this.configPath = path.resolve(__dirname, 'config.json');
        }
        else{
            this.configPath = configPath;
        }
        this.scene = this.buildScene();
    }

    /**
     * Connects to a remote database. 
     * Please create a config.json file in the same directory with the following structure:
     *  {
     *      "fileTestConfigPath": "tests/easytest/FileTestConfig.json",
     *      "gremlinConfig": Your remote database configuration,
     *      "uploadBatchSize": 100,
     *      "logOpen": true
     *  }
     */
    private buildScene(): Scene {
        const configfile = JSON.parse(fs.readFileSync(this.configPath, 'utf-8'));

        const filePath = configfile.fileTestConfigPath;
        if ('uploadBatchSize' in configfile) {
            MAX_BATCH_SIZE = configfile.uploadBatchSize;
        }
        if ('logOpen' in configfile) {
            logger.setOn(configfile.logOpen);
        }
        let config: SceneConfig = new SceneConfig();
        config.buildFromJson(filePath);
        config.buildFromProjectDir(config.getTargetProjectDirectory());
        let scene = new Scene();
        scene.buildSceneFromProjectDir(config);
        return scene;
    }

    /**
     * Creates CPG (Code Property Graph) - serves as the main entry point of the system
     */
    public async buildCPG() {
        this.init();
        await this.buildCfgAndDfg();
        await this.buildInvokeCPG(); 
    }

    /**
     * Performs batch edge upload
     * 
     * @param edges The edges related to the function call statement
     */
    private async processEdges(edges: Edge[]) {
        let cypherBatch = 'g';
        
        for (let i = 0; i < edges.length; i++) {
            cypherBatch += `.addE('${edges[i].type}')
                .from(__.V('${this.stmt2GremlinId.get(edges[i].start)}'))    
                .to(__.V('${this.stmt2GremlinId.get(edges[i].end)}'))
                .property('type', '${edges[i].type}')`;
            if ((i % MAX_BATCH_SIZE === 0 && i !== 0) || i === edges.length - 1) {
                await this.client.submit(cypherBatch);
                cypherBatch = 'g';
            }
        }
    }

    /**
     * Performs initialization of the arkAnalyzer for scene analysis operations
     */
    private init() {
        this.scene.inferTypes();
        let callGraph = new CallGraph(this.scene);
        let callGraphBuilder = new CallGraphBuilder(callGraph, this.scene);
        callGraphBuilder.buildDirectCallGraphForScene();
    }

    /**
     * Concurrently generates Control Flow and Data Flow Graphs per file.
     * Only produces function-call-independent CPG elements due to cross-file call edges.
     */
    private async buildCfgAndDfg() {
        let tasks = []
        for (const arkFile of this.scene.getFiles()) {
            tasks.push(this.buildTask(arkFile));
        }
        await Promise.all(tasks);
        logger.writeFileBuilders();
    }

    /**
     * Constructs the graph for each file and records:
     * - Mapping between functions and their signatures in the file
     * - Mapping between call statements and callee functions  
     * - Mapping between statements and their corresponding IDs in Gremlin
     *
     * @param arkFile The file to be processed
     */
    private async buildTask(arkFile: ArkFile) {
        let fileBuilder = new FileBuilder(arkFile);
        await fileBuilder.upload(this.client);
        logger.addFileBuilder(fileBuilder);
        this.method2MethodStmtUnion(fileBuilder.getMethod2MethodStmt());
        this.field2FieldStmtUnion(fileBuilder.getField2FieldStmt());
        this.invokeStmt2Methods.push(fileBuilder.getInvokeStmt2Method());
        this.fieldAccessStmtss.push(fileBuilder.getFieldAccessStmts());
        this.stmt2GremlinIdUnion(fileBuilder.getStmt2GremlinId());
    }

    private method2MethodStmtUnion(method2MethodStmt: Map<MethodSignature, ArkMethodDefStmt>) {
        for(const [key, value] of method2MethodStmt) {
            this.method2MethodStmt.set(key, value);
        }
    }

    private field2FieldStmtUnion(field2FieldStmt: Map<FieldSignature, ArkFieldDefStmt>) {
        for(const [key, value] of field2FieldStmt) {
            this.field2FieldStmt.set(key, value);
        }
    }

    private stmt2GremlinIdUnion(stmt2GremlinId: Map<Stmt, any>) {
        for(const [key, value] of stmt2GremlinId) {
            this.stmt2GremlinId.set(key, value);
        }
    }

    private async buildInvokeCPG() {
        let tasks = []
        for (const invokeStmt2Method of this.invokeStmt2Methods){
            tasks.push(this.buildInvokeTask(invokeStmt2Method));
        }
        for (const fieldAccessStmts of this.fieldAccessStmtss) {
            tasks.push(this.buildFieldAccessTask(fieldAccessStmts));
        }
        await Promise.all(tasks);
        logger.writeCallEdges();
    }

    /**
     * Constructs edges related to call statements.
     * 
     * @param invokeStmt2Method Mapping between call statements and their invoked methods within a single file
     */
    private async buildInvokeTask(invokeStmt2Method: Map<Stmt, MethodSignature>) {
        let edges: Edge[] = []
        for (const [stmt, methodSignature] of invokeStmt2Method) {
            let method = this.method2MethodStmt.get(methodSignature);
            if (method !== undefined) {
                for (const edge of this.buildReturnCPG(method, stmt)) {
                    edges.push(edge);
                }
                edges.push(new CgEdge(stmt, method))
                edges.push(new CfgEdge(stmt, method))
                edges.push(new DfgEdge(method, stmt))
            }
        }
        logger.addEdges(edges);
        await this.processEdges(edges);
    }

    private async buildFieldAccessTask(fieldAccessStmts: FieldAccessStmt[]) {
        let edges: Edge[] = []
        for (const fieldAccessStmt of fieldAccessStmts) {
            let stmt = this.field2FieldStmt.get(fieldAccessStmt.getFieldSignature());
            if (stmt !== undefined) {
                if (fieldAccessStmt.getIsDef()) {
                    edges.push(new DfgEdge(stmt, fieldAccessStmt.getStmt()));
                }
                else {
                    edges.push(new DfgEdge(fieldAccessStmt.getStmt(), stmt));
                }
            }
        }
        logger.addEdges(edges);
        await this.processEdges(edges);
    }

    private buildReturnCPG(method: ArkMethodDefStmt, stmt: Stmt): Edge[] {
        let edges: Edge[] = [];
        if (method !== undefined) {
            for (const returnstmt of method?.getReturnStmts()) {
                edges.push(new CfgEdge(returnstmt, stmt, "because of function call"));
                edges.push(new DfgEdge(stmt, returnstmt));
            }
        }
        return edges;
    }
}

export class FileBuilder {
    private invokeStmt2Method: Map<Stmt, MethodSignature> = new Map<Stmt, MethodSignature>();
    private fieldAccessStmts: FieldAccessStmt[] = []
    private arkFile: ArkFile;
    private nodes: Node[] = [];
    private edges: Edge[] = [];
    private stmt2Id: Map<Stmt, string> = new Map();
    private id2GremlinId: Map<string, any> = new Map();
    private stmt2GremlinId: Map<Stmt, any> = new Map();
    private method2MethodStmt: Map<MethodSignature, ArkMethodDefStmt> = new Map<MethodSignature, ArkMethodDefStmt>()
    private field2FieldStmt: Map<FieldSignature, ArkFieldDefStmt> = new Map<FieldSignature, ArkFieldDefStmt>();

    /**
     * Traverses all methods in the file to:
     * - Retrieve their corresponding nodes
     * - Process function-call-independent CPG elements
     * - Establish mappings between MethodSignatures and methods
     * - Create call statement to invoked method mappings
     */
    constructor (arkFile: ArkFile) {
        this.arkFile = arkFile;
        for (const arkClass of arkFile.getClasses()) {
            let isArkUI: boolean = this.judgeArkUI(arkClass);
            let arkFields: ArkField[] = arkClass.getFields();
            for (let arkField of arkFields) {
                let arkFieldDefStmt: ArkFieldDefStmt = new ArkFieldDefStmt(arkField);
                this.field2FieldStmt.set(arkField.getSignature(), arkFieldDefStmt);
                this.nodes.push(new Node(arkFieldDefStmt, arkFile, 'field'))
            }

            for (const method of arkClass.getMethods()) {
                if (method.getSignature().getMethodSubSignature().toString() === DEFAULT_METHOD) {
                    continue;  // continue if the method is generated by ArkAnalyzer.
                }
                let isUIBuild = false;
                if (isArkUI) {
                    let methodSignature: MethodSubSignature = method.getSignature().getMethodSubSignature();
                    if (methodSignature.getMethodName() == 'build') {
                        isUIBuild = true;
                    }
                }
                let methodBuilder: MethodBuilder = new MethodBuilder(method, arkFile, isUIBuild);
                this.invokeStmt2MethodUnion(methodBuilder.getInvokeStmt2Method());
                this.fieldAccessStmtsUnion(methodBuilder.getFieldAccessStmts());
                this.method2MethodStmt.set(method.getSignature(), methodBuilder.getMethodStmt());
                
                this.nodes.push(...methodBuilder.getNodes());
                this.edges.push(...methodBuilder.getCfgEdges());
                this.edges.push(...methodBuilder.getDfgEdges());
            }
            if (isArkUI) {
                this.parseArkUILife(arkClass);
            }
        }
    }

    private parseArkUILife(arkClass: ArkClass) {
        let life: string[] = ['constructor', 'aboutToAppear', 'build', 'onDidBuild', 'aboutToDisappear'];
        let lifeMethods: ArkMethod[] = [];
        for (let name of life) {
            for (const method of arkClass.getMethods()) {
                if(name === method.getSignature().getMethodSubSignature().getMethodName()){
                    lifeMethods.push(method);
                }
            }
        }

        for (let i = 0;i<lifeMethods.length;i++) {
            if (i < lifeMethods.length - 1) {
                let front: ArkMethod = lifeMethods[i];
                let nextStmt: Stmt | undefined = this.method2MethodStmt.get(lifeMethods[i + 1].getSignature());
                let blocks: Set<BasicBlock> | undefined = front.getBody()?.getCfg().getBlocks();

                if (nextStmt !== undefined && blocks !== undefined) {
                    for (const block of blocks) {
                        for (let stmt of block.getStmts()) {
                            if (stmt instanceof ArkReturnStmt || stmt instanceof ArkReturnVoidStmt) {
                                this.edges.push(new CfgEdge(stmt, nextStmt));
                            }
                        }
                    }
                }
            }
        }
    }

    private judgeArkUI(arkClass: ArkClass) {
        let decorators: Decorator[] = arkClass.getDecorators();
        for (let decorator of decorators) {
            if (decorator.kind == 'Component') {
                return true;
            }
        }
        return false;
    }

    public getNodes(): Node[] {
        return this.nodes;
    }

    public getEdges(): Edge[] {
        return this.edges;
    }

    public getArkFile(): ArkFile {
        return this.arkFile;
    }

    private fieldAccessStmtsUnion(fieldAccessStmts: FieldAccessStmt[]) {
        for (let fieldAccessStmt of fieldAccessStmts) {
            this.fieldAccessStmts.push(fieldAccessStmt);
        }
    }

    public getFieldAccessStmts() {
        return this.fieldAccessStmts;
    }

    public invokeStmt2MethodUnion(invokeStmt2Method: Map<Stmt, MethodSignature>) {
        for (const [key, value] of invokeStmt2Method) {
            this.invokeStmt2Method.set(key, value);
        }
    }

    public getMethod2MethodStmt(): Map<MethodSignature, ArkMethodDefStmt> {
        return this.method2MethodStmt;
    }

    public getField2FieldStmt(): Map<FieldSignature, ArkFieldDefStmt> {
        return this.field2FieldStmt;
    }

    public getInvokeStmt2Method(): Map<Stmt, MethodSignature> {
        return this.invokeStmt2Method;
    }

    public getStmt2GremlinId(): Map<Stmt, any> {
        return this.stmt2GremlinId;
    }

    public async upload(client: driver.Client) {
        await this.processNodes(client);
        this.parseStmt2GremlinId();
        await this.processEdges(client);
    }

    /**
     * Uploads nodes in batches and establishes mappings between statements and their corresponding IDs
     */
    private async processNodes(client: driver.Client) {
        let count = 0;
        let cypherBatch = 'g';
        let select: string[] = [];
        for (let i = 0; i < this.nodes.length; i++) {
            const node: Node = this.nodes[i];
            this.stmt2Id.set(node.getStmt(), node.getId());
            select.push('\'' + node.getId() + '\'');
            cypherBatch += node.getCypherBatch();
            count++;
            if (count % MAX_BATCH_SIZE === 0 || i === this.nodes.length - 1) {
                await this.submitBatch(select, cypherBatch, client);
                cypherBatch = 'g';
                select = [];
            }
        }
    }

    /**
     * Uploads nodes to the database and generates a mapping between local IDs and Gremlin-allocated IDs.
     */
    private async submitBatch(select: string[], cypherBatch: string, client: driver.Client) {
        cypherBatch += `.select(${select.join(',')})`;
        await client.submit(cypherBatch).then(res => {
            if (res.length === 0) {
                return ;
            }
            if (!(res._items[0] instanceof Map)) {
                let idname = select[0].replace(/[^a-zA-Z0-9_]/g, '');
                res._items[0] = new Map([[idname, res._items[0]]]);
            }
            res._items[0].forEach((value: any, key: any) => {
                this.id2GremlinId.set(key, value);
            });
        });
    }

    private parseStmt2GremlinId() {
        this.stmt2Id.forEach((id, stmt) => {
            this.stmt2GremlinId.set(stmt, this.id2GremlinId.get(id));
        });
    }

    private async processEdges(client: driver.Client) {
        let cypherBatch = 'g';
        for (let i = 0; i < this.edges.length; i++) {
            cypherBatch += `.addE('${this.edges[i].type}')
                .from(__.V('${this.stmt2GremlinId.get(this.edges[i].start)}'))    
                .to(__.V('${this.stmt2GremlinId.get(this.edges[i].end)}'))
                .property('type', '${this.edges[i].type}')`;
            if ((i % MAX_BATCH_SIZE === 0 && i !== 0) || i === this.edges.length - 1) {
                await client.submit(cypherBatch);
                cypherBatch = 'g';
            }
        }
    }
}

export class FieldAccessStmt {
    private stmt: Stmt;
    private fieldSignature: FieldSignature;
    private isDef: boolean;

    constructor(stmt: Stmt, fieldSignature: FieldSignature, isDef: boolean) {
        this.stmt = stmt;
        this.fieldSignature = fieldSignature;
        this.isDef = isDef;
    }

    public equals(fieldAccessStmt: FieldAccessStmt): boolean {
        return this.fieldSignature === fieldAccessStmt.fieldSignature &&
            this.stmt === fieldAccessStmt.stmt && this.isDef === fieldAccessStmt.isDef;
    }

    public getStmt(): Stmt {
        return this.stmt;
    }

    public getFieldSignature(): FieldSignature {
        return this.fieldSignature;
    }

    public getIsDef(): boolean {
        return this.isDef;
    }
}

export class MethodBuilder {
    private methodStmt: ArkMethodDefStmt;
    private InvokeStmt2Method: Map<Stmt, MethodSignature> = new Map<Stmt, MethodSignature>();
    private fieldAccessStmts: FieldAccessStmt[] = [];
    private nodes: Node[] = [];
    private cfgEdges: CfgEdge[] = [];
    private dfgEdges: DfgEdge[] = [];
    private methodSignature: string;

    /**
     * Performs all analysis during MethodBuilder instantiation including:
     * - Extracting method signatures
     * - Collecting all relevant nodes 
     * - Gathering function-call-independent edges
     * - Building call statement to callee function mappings
     */
    constructor (arkMethod: ArkMethod, arkFile: ArkFile, isArkUI: boolean) {
        this.methodSignature = arkMethod.getSignature().toString().replace(/[^a-zA-Z0-9_]/g, '');
        this.methodStmt = new ArkMethodDefStmt(arkMethod);
        this.nodes.push(new Node(this.methodStmt, arkFile, this.methodSignature));
        this.addParametersDFG(arkMethod, this.methodStmt);
        this.parseBody(arkMethod.getBody(), arkFile, this.methodSignature);

        this.cfgEdges = this.filteEdges(this.cfgEdges);
        if (isArkUI) {
            this.addCycleEdge(arkMethod.getBody());
        }
        let dfgBuilder: DfgBuilder = new DfgBuilder(this.cfgEdges);
        this.dfgEdges.push(...dfgBuilder.getDfgEdges());
        this.dfgEdges = this.filteEdges(this.dfgEdges);
    }

    public addFieldAccessStmt(fieldAccessStmt: FieldAccessStmt) {
        if (!this.fieldAccessStmts.some(item => item.equals(fieldAccessStmt))) {
            this.fieldAccessStmts.push(fieldAccessStmt);
        }
    }

    public getFieldAccessStmts(): FieldAccessStmt[] {
        return this.fieldAccessStmts;
    }

    public getInvokeStmt2Method(): Map<Stmt, MethodSignature> {
        return this.InvokeStmt2Method;
    }

    public getMethodStmt(): ArkMethodDefStmt {
        return this.methodStmt;
    }

    public getNodes(): Node[] {
        return this.nodes;
    }

    public getCfgEdges(): CfgEdge[] {
        return this.cfgEdges;
    }

    public getDfgEdges(): DfgEdge[] {
        return this.dfgEdges;
    }

    /**
     * Deduplicates the input edges.
     */
    private filteEdges(edges: Edge[]): Edge[] { 
        let filted: Edge[] = [];
        for (let i = 0; i < edges.length; i++) {
            let flag = false;
            if (edges[i].start === undefined || edges[i].end === undefined) {
                flag = true;
                continue;
            }
            for (let j = 0; j < i; j++) {
                if (edges[i].equals(edges[j])) {
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                filted.push(edges[i]);
            }
        }
        return filted;
    }

    private addCycleEdge(arkBody: ArkBody | undefined) {
        if (arkBody === undefined) {
            return ;
        }
        const blocks = [...arkBody.getCfg().getBlocks()];
        if (blocks.length === 0) {
            return ;
        }
        let start: Stmt = blocks[0].getStmts()[0];

        let end: Stmt | undefined = undefined;
        for (const block of blocks) {
            if (block.getStmts().length > 0) {
                end = block.getStmts()[block.getStmts().length - 1];
            }
        }
        if (end !== undefined) {
            this.cfgEdges.push(new CfgEdge(end, start, 'build cfg of ArkUI'));
        }
    }

    private parseBody(arkBody: ArkBody | undefined, arkFile: ArkFile, methodSignature: string) {
        if (arkBody === undefined) {
            return ;
        }
        const blocks = [...arkBody.getCfg().getBlocks()];
        // link the first block with method
        if (blocks.length > 0){
            this.cfgEdges.push(new CfgEdge(this.methodStmt, blocks[0].getStmts()[0], "the first stmt of the first block in the method"));
        }

        for (const block of blocks) {
            for (let i = 0; i < block.getStmts().length; i++) {
                const stmt = block.getStmts()[i];

                let def = stmt.getDef();
                if (def && def instanceof AbstractFieldRef) {
                    let abstractFieldRef: AbstractFieldRef = def as AbstractFieldRef;
                    let fieldSignature: FieldSignature = abstractFieldRef.getFieldSignature();
                    this.addFieldAccessStmt(new FieldAccessStmt(stmt, fieldSignature, true))
                }

                let fieldRef = stmt.getFieldRef();
                if (fieldRef !== undefined) {
                    let abstractFieldRef: AbstractFieldRef = fieldRef as AbstractFieldRef;
                    let fieldSignature: FieldSignature = abstractFieldRef.getFieldSignature();
                    this.addFieldAccessStmt(new FieldAccessStmt(stmt, fieldSignature, false));
                }

                this.nodes.push(new Node(stmt, arkFile, methodSignature));
                this.buildSingleCfgEdge(block, stmt, i);
                this.buildAnonymityMethodCallGraph(stmt);

                if (stmt instanceof ArkReturnStmt) {
                    this.methodStmt.addReturnStmt(stmt);
                } else if (stmt instanceof ArkReturnVoidStmt) {
                    this.methodStmt.addReturnStmt(stmt);
                }
            }
        }
    }

    private buildSingleCfgEdge(block: BasicBlock, stmt: Stmt, i: number) {
        let invokeExpr = stmt.getInvokeExpr();
        if (invokeExpr !== undefined) {
            this.handleCallCfg(stmt, invokeExpr)
        }
        if (i < block.getStmts().length - 1) {
            this.cfgEdges.push(new CfgEdge(stmt, block.getStmts()[i + 1], "statements before and after within a basic block"))
        }
        else {
            for (const next of block.getSuccessors()) {
                const endtStmt = next.getStmts()[0];
                const startStmt = block.getStmts()[block.getStmts().length - 1];
                if (endtStmt !== undefined && startStmt !== undefined) {
                    this.cfgEdges.push(new CfgEdge(startStmt, endtStmt, "jumps between different basic blocks"));
                }
            }
        }
    }

    private buildAnonymityMethodCallGraph(stmt: Stmt) {
        if (stmt instanceof ArkAssignStmt) {
            let arkAssignStmt = stmt as ArkAssignStmt;
            if (arkAssignStmt.getLeftOp().getType() instanceof FunctionType
                && arkAssignStmt.getRightOp().getType() instanceof FunctionType) {
                let type = arkAssignStmt.getRightOp().getType() as FunctionType;
                let methodSignature = type.getMethodSignature()
                this.InvokeStmt2Method.set(stmt, methodSignature)
            }
        }
    }

    private handleCallCfg(stmt: Stmt, invokeExpr: AbstractInvokeExpr) {
        if (invokeExpr === undefined) {
            return;
        }

        const methodSignature = invokeExpr.getMethodSignature();
        this.InvokeStmt2Method.set(stmt, methodSignature);

        for (const arg of invokeExpr.getArgs()) {
            if (arg instanceof Local) {
                const type = (arg as Local).getType();
                if (type instanceof FunctionType) {
                    this.InvokeStmt2Method.set(stmt, type.getMethodSignature());
                }
            }
        }
    }

    private addParametersDFG(method: ArkMethod, methodstmt: ArkMethodDefStmt) {
        for (const parameter of method.getParameterInstances()) {
            if (parameter.constructor.name === "Local") {
                let local = parameter as Local;
                let stmt = local.getDeclaringStmt();
                if (stmt !== null) {
                    let edge: DfgEdge = new DfgEdge(stmt, methodstmt);
                    this.dfgEdges.push(edge);
                }
            }
        }
    }
}

